package quarto.model;

import quarto.controller.Protocol;

/**
 * Representatie van een speelstuk met 4 eigenschappen
 * @knows
 * - De vier eigenschappen (kleur, grootte, vorm, dichtheid)
 * @author Frank Borgonjen & Tim Smit
 */
public class Piece 
{
    /** mask voor dark */
    public static final int DARK   = 8;
    
    /** mask voor big */
    public static final int BIG    = 4;
    
    /** mask voor circel */
    public static final int CIRCLE = 2;
    
    /** mask voor een gat */
    public static final int HOLE   = 1;
    
    /** textuele omschrijving van donker/licht */
    public static final String DARK_TRUE  = "donkere, ";
    public static final String DARK_FALSE = "lichte, ";
    
    /** textuele omschrijving van groot/klein */
    public static final String BIG_TRUE   = "grote, ";
    public static final String BIG_FALSE  = "kleine, ";
    
    /** textuele omschrijving van circel/vierkant */
    public static final String CIRCLE_TRUE  = "cirkel";
    public static final String CIRCLE_FALSE = "vierkant";
    
    /** textuele omschrijving van open/dicht */
    public static final String HOLE_TRUE    = "open ";
    public static final String HOLE_FALSE   = "dichte ";
    
    /** donker of licht */
    private boolean isDark;
    
    /** groot of klein */
    private boolean isBig;
    
    /** cirkel of vierkant */
    private boolean isCircle;
    
    /** zit er een gat in (dichtheid) */
    private boolean hasHole;
    
    /**
     * Creeert een stuk met vier eigenschappen
     * @param int dark
     * @param int big
     * @param int circle
     * @param int hole
     */
    public Piece(int dark, int big, int circle, int hole)
    {
        this.constructPiece(dark == 1, big == 1, circle == 1, hole == 1);
    }
    
    /**
     * constueerst een stuk aan de hand van de decimale waarde
     * @param decimalRepresentation
     */
    public Piece(int decimalRepresentation) throws InvalidPieceValueException
    {
        if (decimalRepresentation >= 0 && decimalRepresentation <= 15)
        {
            this.constructPiece((decimalRepresentation & DARK)   > 0,
                    (decimalRepresentation & BIG)    > 0,
                    (decimalRepresentation & CIRCLE) > 0,
                    (decimalRepresentation & HOLE)   > 0);
        }
        else
        {
            throw new InvalidPieceValueException("just catch the error!");
        }
    }

    /**
     * Creeert een stuk met vier eigenschappen
     * @param String dark
     * @param String big
     * @param String circle
     * @param String hole
     */
    public Piece(String dark, String big, String circle, String hole)
    {
        this.constructPiece(dark.equals(Protocol.TRUE),
                big.equals(Protocol.TRUE),
                circle.equals(Protocol.TRUE),
                hole.equals(Protocol.TRUE));
    }
    
    /**
     * Creeert een stuk aan de hand van 4 boolean waarden
     * @param dark
     * @param big
     * @param circle
     * @param hole
     */
    private void constructPiece(boolean dark, boolean big, boolean circle, boolean hole)
    {
        this.isDark = dark;
        this.isBig = big;
        this.isCircle = circle;
        this.hasHole = hole;
    }
    
    /**
     * Zet het stuk om naar een decimaal getal, dit
     * om het vergelijken van de eigenschappen makkelijker
     * te maken. Dit wordt makkelijker doordat er bitwise
     * vergeleken kan worden. In dit geval houdt dat in
     * dat de vier verschillende eigenschappen tegelijk
     * vergeleken kunnen worden.
     * @return int decimale representatie van het stuk
     */
    public int toDecimal()
    {
        int result = 0;
        
        // gebruik machten van 2 om steeds een volgende
        // bit te pakken
        if (isDark)   result += DARK;
        if (isBig)    result += BIG;
        if (isCircle) result += CIRCLE;
        if (hasHole)  result += HOLE;
        
        return result;
    }
    
    /**
     * Kijkt of dit stuk gelijk is aan het stuk wat
     * bedoeld wordt met de meegegeven waarden. Dit zijn
     * Strings omdat dit zo binnen komt van de stream
     * @param piece
     * @return boolean zijn de twee stukken gelijk
     */
    public boolean compare(Piece piece)
    {
        return piece != null && this.toDecimal() == piece.toDecimal();
    }
    
    /**
     * Geeft een textuele vorm van het stuk
     * @return
     */
    @Override
    public String toString()
    {
        return "" + this.toDecimal();
    }
    
    /**
     * Geeft een String representatie van dit Piece object dat gebruikt kan
     * worden bij het versturen over het netwerk. Deze bestaat uit elementen
     * uit het Protocol
     * @return String representatie van Piece voor netwerk communicatie
     */
    public String getNetworkNotation()
    {
        String res = "";
        res += (isDark)   ? Protocol.TRUE : Protocol.FALSE;
        res += Protocol.DELIM;
        res += (isBig)    ? Protocol.TRUE : Protocol.FALSE;
        res += Protocol.DELIM;
        res += (isCircle) ? Protocol.TRUE : Protocol.FALSE;
        res += Protocol.DELIM;
        res += (hasHole)  ? Protocol.TRUE : Protocol.FALSE;
        return res;
    }
    
    /**
     * Geeft en textuele omschrijving van het stuk
     * @return omschrijving
     */
    public String getDescription()
    {
        String description = (isCircle) ? "de " : "het ";
        description += (isDark)   ? DARK_TRUE   : DARK_FALSE;
        description += (isBig)    ? BIG_TRUE    : BIG_FALSE;
        description += (hasHole)  ? HOLE_TRUE   : HOLE_FALSE;
        description += (isCircle) ? CIRCLE_TRUE : CIRCLE_FALSE;
        return description;
    }
}
